package calculator.view;


import calculator.model.*;

import java.awt.Insets;
import java.awt.Color;
import java.awt.Font;
import java.awt.Dimension;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.BorderFactory;

import java.util.List;
import java.util.ArrayList;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.awt.event.ItemEvent;

import javax.swing.text.StyledDocument;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.rtf.RTFEditorKit;

import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.File;


// This class opens a window containing text areas and buttons used to display and manipulate existing data.
public class Gui {
    

    public static void main(String[] args) {
        
        // This code passes a path of a directory containing all stored data.
        String productsFilePath = "resources/products.txt";
        new Gui(productsFilePath);
    }
    

    private ProductModel productModel;
    
    private JFrame f;
    private JPanel buttonsPanel;
    private JPanel centralPanel;
    
    private JPanel mainPanel;    
    private JPanel userPanel;
    private JPanel managerPanel;
    private JPanel descriptionPanel;
    
    private JPanel userDetailsPanel;
    private JPanel userListPanel;
    private JPanel northManagerPanel;
    private JPanel southManagerPanel;
    
    private JButton[] optionButtons;
    private JButton[] outputButtons;
    
    private JTextArea userTextArea;
    private JTextArea outputTextArea;
    private JTextPane outputTextPane;
    private JScrollPane outputScrollPane;
    private RTFEditorKit kit;
    private StyledDocument dsd;
    
    private JComboBox heightComboBox;
    private JComboBox weightComboBox;
    private JComboBox ageComboBox;
    private JComboBox genderComboBox;
    private JComboBox frequencyComboBox;
    private JComboBox durationComboBox;
    private JComboBox activityComboBox;        
    
    private JComboBox nameComboBox;
    private JTextField mlTextField;
    private JTextField percentageTextField;
    private JComboBox quantityComboBox;
    
    private List<JTextField> managerPanelTextFields;
    
    
    Gui(String productsFilePath){
        productModel = new ProductModel(productsFilePath);
        launchFrame();
    }
    

    private void launchFrame() {                 
        
        loadButtonsPanel();
        loadCentralPanel();        
        
        f = new JFrame("Kalkulatory");        
                
        f.setLayout(new BoxLayout(f.getContentPane(), BoxLayout.PAGE_AXIS));
        f.add(Box.createVerticalStrut(15));
        f.add(buttonsPanel);
        f.add(Box.createVerticalStrut(30));
        f.add(centralPanel);
        f.add(Box.createVerticalStrut(15));
        
        f.pack();
        f.getContentPane().setBackground(new Color(225,225,200));
        f.setResizable(false);
        f.setLocation(0, 0);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
        f.setVisible(true);
        
    }
    
    
    // Launches a panel containing buttons used to display different bits of the application.
    // The buttons switch components dispalayed in the central panel.
    private void loadButtonsPanel() {
        
        buttonsPanel = new JPanel();
        buttonsPanel.setBackground(new Color(225,225,200));
        buttonsPanel.setLayout(new FlowLayout());
        
        optionButtons = new JButton[] {    
            new JButton("Alkomat"),
            new JButton("Kalkulator kalorii"),
            new JButton("Administracja"),
            new JButton("Opis programu"),
        };        
        
        for (JButton button : optionButtons) {
            button.setBackground(new Color(200,200,175));
            button.setForeground(Color.RED);
            button.setPreferredSize(new Dimension(160, 35));
            button.setFont(new Font("TimesNewRoman", Font.BOLD, 14));
            button.addActionListener(new optionButtonsHandler());
            buttonsPanel.add(button);            
        }
        
        optionButtons[0].setSelected(true);
        
    }
    
    
    // Launches a panel with text areas that are used to display outputs.
    private void loadCentralPanel() {
        
        loadMainPanel();
        
        kit = new RTFEditorKit();
        dsd = new DefaultStyledDocument();        
        
        // Define a text pane that is later used by the description panel.
        outputTextPane = new JTextPane();
        outputTextPane.setBackground(Color.WHITE);
        outputTextPane.setMargin(new Insets(4, 5, 4, 5));
        outputTextPane.setEditable(false);        
                
        outputTextArea = new JTextArea();
        outputTextArea.setBackground(Color.WHITE);
        outputTextArea.setMargin(new Insets(4, 5, 4, 5));
        outputTextArea.setFont(new Font("TimesNewRoman", Font.BOLD, 12));
        outputTextArea.setEditable(false);
        
        outputScrollPane = new JScrollPane(outputTextArea);
        outputScrollPane.setPreferredSize(new Dimension( 330, 0 ));
                
        centralPanel = new JPanel();        
        centralPanel.setBackground(new Color(225,225,200));
        centralPanel.setLayout(new BoxLayout(centralPanel, BoxLayout.LINE_AXIS));
        
        centralPanel.add(Box.createHorizontalStrut(15));
        centralPanel.add(outputScrollPane);
        centralPanel.add(Box.createHorizontalStrut(20));
        centralPanel.add(mainPanel);
        centralPanel.add(Box.createHorizontalStrut(20));
        
    }    
    
    
    // Prepares a helper panel that is used to display user, manager and description panels.
    private void loadMainPanel() {
        
        loadUserPanel();
        loadManagerPanel();
        loadDescriptionPanel();
                
        mainPanel = new JPanel();        
        mainPanel.setPreferredSize(new Dimension(300, 580));        
        mainPanel.setBackground(new Color(225,225,200));
        mainPanel.setLayout(new BorderLayout());         
                        
        mainPanel.add(userPanel, BorderLayout.CENTER);
        
    }  
    
    
    // Prepares a panel that is used to create a list of products.
    private void loadUserPanel() {
        
        loadUserDetailsPanel();
        loadUserListPanel();
                
        userPanel =new JPanel();
        userPanel.setBackground(new Color(225,225,200));
        userPanel.setLayout(new BoxLayout(userPanel, BoxLayout.PAGE_AXIS));        
        
        userPanel.add(userDetailsPanel);
        userPanel.add(Box.createVerticalStrut(20));
        userPanel.add(userListPanel);
        
    }
    
    
    //Prepares a panel that is used to manipulate existing data.
    private void loadManagerPanel() {
        
        loadNorthManagerPanel();
        loadSouthManagerPanel();
        
        managerPanel =new JPanel();
        managerPanel.setBackground(new Color(225,225,200));
        managerPanel.setLayout(new BoxLayout(managerPanel, BoxLayout.PAGE_AXIS));
        managerPanel.setBorder(BorderFactory.createMatteBorder(
                                    0, 5, 0, 5, new Color(225,225,200)));
        
        managerPanel.add(northManagerPanel);
        managerPanel.add(Box.createVerticalStrut(5));
        managerPanel.add(southManagerPanel);
        managerPanel.add(Box.createVerticalStrut(120));
        
    }
    
    
    //Prepares a panel that is used to display descriptions.
    private void loadDescriptionPanel() {
        
        descriptionPanel =new JPanel();
        descriptionPanel.setBackground(new Color(225,225,200));
        descriptionPanel.setLayout(new FlowLayout());
        
        JButton[] descriptionButtons = new JButton[] {            
            new JButton("Informacje ogólne"),
            new JButton("Okno programu"),
            new JButton("Panel administratora"),
            new JButton("Alkomat - działanie"),
            new JButton("Alkomat - algorytm"),
            new JButton("Alkomat - teoria"),            
            new JButton("Kalkulator kalorii - działanie"),                        
            new JButton("Kalkulator kalorii - algorytm"),
            new JButton("Kalkulator kalorii - teoria"),
            new JButton("Cechy wyróżniające program"),
            new JButton("Możliwe modyfikacje"),
            new JButton("Autor"),            
            new JButton("Licencja"),
            new JButton("Źródła informacji"),
        };        
        
        for (JButton button : descriptionButtons) {
            button.setBackground(new Color(200,200,175));
            button.setForeground(new Color(0,0,150));
            button.setPreferredSize(new Dimension(260, 30));
            button.setFont(new Font("TimesNewRoman", Font.BOLD, 13));
            button.addActionListener(new ShowDescriptionHandler());
            descriptionPanel.add(button);            
        }
        
    }
    
    // Defines numerous ComboBoxes that let a user to enter informations such as age, height etc. 
    private void loadUserDetailsPanel() {
        
        userDetailsPanel = new JPanel();
        userDetailsPanel.setLayout(new FlowLayout());
        userDetailsPanel.setPreferredSize(new Dimension(300,190));
        userDetailsPanel.setBackground(new Color(225,225,200));
        TitledBorder titled = BorderFactory.createTitledBorder
                (BorderFactory.createLineBorder(new Color(0,0,150)), "Informacje o użytkowniku");
        titled.setTitleColor(new Color(0,0,150));
        Border compound = BorderFactory.createCompoundBorder
                (titled, BorderFactory.createLineBorder(new Color(225,225,200), 5));
        userDetailsPanel.setBorder(compound);      
        
        JLabel[] userDetailsLabels = {
            new JLabel("Wzrost (cm):"),
            new JLabel("          Wiek:"),
            new JLabel("Waga (kg):"),
            new JLabel("          Płeć:"),
            new JLabel("Długość picia (godz.):"),
            new JLabel("Częstotliwość picia:"),
            new JLabel("Aktywność fizyczna:")
        };       
        
        heightComboBox = new JComboBox();
        for ( int i=120; i<=250; i++) {
            heightComboBox.addItem(i);            
        }
        heightComboBox.setSelectedItem(180);
        
        ageComboBox = new JComboBox();
        for ( int i=18; i<=120; i++) {
            ageComboBox.addItem(i);            
        }
        ageComboBox.setSelectedItem(30);
        
        weightComboBox = new JComboBox();
        for ( int i=40; i<=250; i++) {
            weightComboBox.addItem(i);            
        }
        weightComboBox.setSelectedItem(60);
        
        String[] genderItems = {"M", "K"};
        genderComboBox = new JComboBox(genderItems);
        
        durationComboBox = new JComboBox();
        for ( double i=0.0; i<=40; i=i+0.5) {
            durationComboBox.addItem(i);            
        }
        
        String[] frequencyItems = {"często", "zwyczajnie", "rzadko"};
        frequencyComboBox = new JComboBox(frequencyItems);
        
        String[] typeOfWorkItems = {"bardzo duża", "duża", "umiarkowana", "mała", "żadna"};
        activityComboBox = new JComboBox(typeOfWorkItems);
        activityComboBox.setEnabled(false);
        
        // Add the ComboBoxes to an array so that we can Set the panel using do loops.
        JComboBox[] userDetailsArray = {heightComboBox, ageComboBox, weightComboBox, genderComboBox,
                                        durationComboBox, frequencyComboBox, activityComboBox};
        
        // First two lines of the panel contain different number of components than the last three
        // so we deal with them separately.
        for (int i=0; i<4; i=i+2) {
            
            JPanel comboBoxPanel = new JPanel();
            comboBoxPanel.setLayout(new BoxLayout(comboBoxPanel, BoxLayout.LINE_AXIS));
            comboBoxPanel.setBackground(new Color(225,225,200));
            
            for (int j=i; j<i+2; j++) {
                userDetailsLabels[j].setPreferredSize(new Dimension(85, 25));            
                userDetailsArray[j].setPreferredSize(new Dimension(55, 25));       
                comboBoxPanel.add(userDetailsLabels[j]);
                comboBoxPanel.add(userDetailsArray[j]);
            }
            
            userDetailsPanel.add(comboBoxPanel);
            
        }
        
        // Set the last three lines of the panel.
        for (int i=4; i<7; i++) {
            
            userDetailsArray[i].setPreferredSize(new Dimension(110, 25));
        
            JPanel comboBoxPanel = new JPanel();
            comboBoxPanel.setPreferredSize(new Dimension(280, 25));
            comboBoxPanel.setLayout(new BorderLayout());
            comboBoxPanel.setBackground(new Color(225,225,200));
            comboBoxPanel.add(userDetailsLabels[i], BorderLayout.WEST);
            comboBoxPanel.add(userDetailsArray[i], BorderLayout.EAST);
        
            userDetailsPanel.add(comboBoxPanel);
            
        }        
         
    }
    
    
    // Defines a panel that enable a user to create a list of products. 
    private void loadUserListPanel() {        
        
        // Define a ComboBox that contains all stored data.
        JLabel namesLabel = new JLabel("Produkt:");       
        nameComboBox = new JComboBox();
        nameComboBox.setPreferredSize(new Dimension(200, 25));        
        setNewComboBoxItems (); 
        nameComboBox.addItemListener(new ItemListener() {            
            public void itemStateChanged(ItemEvent evt) {
                
                JComboBox cb = (JComboBox) evt.getSource();
                String item = (String)cb.getSelectedItem();
                
                // Select "Twój produkt" to be able to define your own capacity and percentage for alcoholic products.
                if (evt.getStateChange() == ItemEvent.SELECTED) {
                    if ( item.equals("Twój produkt") ) {
                        mlTextField.setEditable(true);
                        percentageTextField.setEditable(true);
                    } else {
                        mlTextField.setEditable(false);
                        percentageTextField.setEditable(false);
                    }
                }
                
            } 
        });
        
        JPanel namesPanel = new JPanel();
        namesPanel.setLayout(new BorderLayout());
        namesPanel.setPreferredSize(new Dimension(280, 25));
        namesPanel.setBackground(new Color(225,225,200));
        namesPanel.add(namesLabel, BorderLayout.WEST);
        namesPanel.add(nameComboBox, BorderLayout.EAST);
        
        JLabel mlLabel = new JLabel("Poj. (ml):");
        mlTextField = new JTextField();
        mlTextField.setPreferredSize(new Dimension(55, 25));
        
        JLabel percentageLabel = new JLabel("Alkohol (%):");
        percentageTextField = new JTextField();
        percentageTextField.setPreferredSize(new Dimension(55, 25));
                
        JPanel yourValuePanel = new JPanel();
        yourValuePanel.setLayout(new BoxLayout(yourValuePanel, BoxLayout.LINE_AXIS));
        yourValuePanel.setPreferredSize(new Dimension(280, 25));
        yourValuePanel.setBackground(new Color(225,225,200));
        yourValuePanel.add(mlLabel);
        yourValuePanel.add(Box.createHorizontalStrut(30));
        yourValuePanel.add(mlTextField);
        yourValuePanel.add(Box.createHorizontalStrut(26));
        yourValuePanel.add(percentageLabel);
        yourValuePanel.add(Box.createHorizontalStrut(10));        
        yourValuePanel.add(percentageTextField);
        
        JLabel quantityLabel = new JLabel("Liczba sztuk:");
        quantityComboBox = new JComboBox();
        quantityComboBox.setPreferredSize(new Dimension(50, 25));
        for ( int i=1; i<=99; i++) {
            quantityComboBox.addItem(i);            
        }
        
        JPanel quantityPanel = new JPanel();        
        quantityPanel.setLayout(new BorderLayout());
        quantityPanel.setPreferredSize(new Dimension(280, 25));
        quantityPanel.setBackground(new Color(225,225,200));        
        quantityPanel.add(quantityLabel, BorderLayout.WEST);        
        quantityPanel.add(quantityComboBox, BorderLayout.EAST);       
       
        // Define buttons that enable a used to create a list of products.
        JButton[] userListButtons = new JButton[] {    
            new JButton("Dodaj produkt do listy"),
            new JButton("Usuń wszystkie produkty"),
        };
        
        userListButtons[0].addActionListener(new AddProductHandler());
        userListButtons[1].addActionListener(new RemoveAllProductsHandler());
        
        for (JButton button : userListButtons) {
            button.setBackground(new Color(200,200,175));
            button.setForeground(new Color(0,0,150));
            button.setPreferredSize(new Dimension(220, 30));
            button.setFont(new Font("TimesNewRoman", Font.BOLD, 13));                        
        }
        
        // Define a text area used to display a list of products created by a user.
        userTextArea = new JTextArea();                      
        userTextArea.setEditable(false);
        userTextArea.setLineWrap(true);
        userTextArea.setMargin(new Insets(4, 5, 4, 5));
        userTextArea.setFont(new Font("TimesNewRoman", Font.BOLD, 12));
        JScrollPane userScrollPane = new JScrollPane(userTextArea);        
        userScrollPane.setPreferredSize(new Dimension( 280, 130 ));
        
        // Define buttons that enable a used to get outputs for previously created lists of products.
        outputButtons = new JButton[] {    
            new JButton("Sprawdź zawartość alkoholu"),
            new JButton("Sprawdź kaloryczność"),
        };
        
        outputButtons[0].addActionListener(new GetAlcoholContentHandler());
        outputButtons[1].addActionListener(new GetCaloriesHandler());
        
        for (JButton button : outputButtons) {
            button.setPreferredSize(new Dimension(220, 30));
            button.setBackground(new Color(200,200,175));
            button.setForeground(new Color(0,0,150));
            button.setFont(new Font("TimesNewRoman", Font.BOLD, 13));                   
        }
        
        userListPanel = new JPanel();
        userListPanel.setPreferredSize(new Dimension(300, 370));        
        userListPanel.setBackground(new Color(225,225,200));
        TitledBorder titled = BorderFactory.createTitledBorder
                (BorderFactory.createLineBorder(new Color(0,0,150)), "Lista produktów");
        titled.setTitleColor(new Color(0,0,150));
        Border compound = BorderFactory.createCompoundBorder
                (titled, BorderFactory.createLineBorder(new Color(225,225,200), 5));
        userListPanel.setBorder(compound);
        
        userListPanel.setLayout(new FlowLayout());        
        userListPanel.add(namesPanel);
        userListPanel.add(yourValuePanel);
        userListPanel.add(quantityPanel);
        userListPanel.add(userListButtons[0]);
        userListPanel.add(userListButtons[1]);
        userListPanel.add(userScrollPane);
        userListPanel.add(outputButtons[0]);        
        
    }
    
    
    // Prepares forms for the managers panel.
    private void loadNorthManagerPanel() {
        
        northManagerPanel = new JPanel();
        northManagerPanel.setLayout(new BoxLayout(northManagerPanel, BoxLayout.PAGE_AXIS));
        northManagerPanel.setBackground(new Color(225,225,200));
        
        JLabel[] managerPanelLabels = {
            new JLabel("Nazwa produktu:"),
            new JLabel("Jednostka miary:"),
            new JLabel("Kalorie (kcal):"),
            new JLabel("Białko (g):"),
            new JLabel("Tłuszcz (g):"),
            new JLabel("Węglowodany (g):"),
            new JLabel("Alkohol (%):"),
        };        
        
        managerPanelTextFields = new ArrayList<JTextField>();
        
        for (int i=0; i<managerPanelLabels.length; i++) {                      
            
            JTextField formField = new JTextField();
            formField.setPreferredSize(new Dimension(170, 25));
            managerPanelTextFields.add(formField);
            
            JPanel inputPanel = new JPanel();
            inputPanel.setLayout(new BorderLayout());
            inputPanel.setBackground(new Color(225,225,200));
            inputPanel.add(managerPanelLabels[i], BorderLayout.WEST);
            inputPanel.add(managerPanelTextFields.get(i), BorderLayout.EAST);
            
            northManagerPanel.add(inputPanel);
            northManagerPanel.add(Box.createVerticalStrut(5));
        } 
        
    }
    
    
    // Prepares buttons for the managers panel.
    private void loadSouthManagerPanel() {
        
        southManagerPanel = new JPanel();
        southManagerPanel.setPreferredSize(new Dimension(310, 250));
        southManagerPanel.setBackground(new Color(225,225,200));
        southManagerPanel.setLayout(new FlowLayout());
        
        JButton[] productsButtons = new JButton[] {            
            new JButton("Dodaj nowy produkt do listy"),
            new JButton("Modyfikuj produkt"),
            new JButton("Usuń produkt z listy"),
            new JButton("Pokaż wybrany produkt"),
            new JButton("Pokaż wszystkie produkty"),
            new JButton("Pokaż produkty alkoholowe"),
            new JButton("Pokaż produkty niealkoholowe"),
        };
        
        productsButtons[0].addActionListener(new AddNewProductHandler());
        productsButtons[1].addActionListener(new ModifyProductHandler());
        productsButtons[2].addActionListener(new DeleteExistingProductHandler());
        productsButtons[3].addActionListener(new ShowChosenProductHandler());
        productsButtons[4].addActionListener(new ShowAllProductsHandler());
        productsButtons[5].addActionListener(new ShowAllAlcoholicProductsHandler());
        productsButtons[6].addActionListener(new ShowAllNonAlcoholicProductsHandler());
        
        for (JButton button : productsButtons) {
            button.setBackground(new Color(200,200,175));
            button.setForeground(new Color(0,0,150));
            button.setPreferredSize(new Dimension(260, 30));
            button.setFont(new Font("TimesNewRoman", Font.BOLD, 13));
            southManagerPanel.add(button);            
        }
        
    }
        
    // When one of the option buttons is clicked, content displayed in the central panel changes.
    class optionButtonsHandler implements ActionListener {
       
        public void actionPerformed(ActionEvent e) {
            
            JButton clickedButton = (JButton)e.getSource();            
            
            // optionButtons[3] should take you to the description panel.
            if( clickedButton.equals(optionButtons[3]) ) {
                
                outputTextPane.setText("");
                
                // Note that the outputTextPane is only used by the descriptionPanel to display longer pieces of text saved in rtf files.
                if ( outputTextArea.isShowing() ) {
                    outputScrollPane.remove(outputTextArea);                
                    outputScrollPane.repaint();
                    outputScrollPane.getViewport().add(outputTextPane);
                    outputScrollPane.validate();
                    
                    mainPanel.removeAll();
                    mainPanel.repaint();
                    mainPanel.add(descriptionPanel, BorderLayout.CENTER);
                    mainPanel.validate();                    
                }
                
            } else {
                
                outputTextArea.setText("");
                
                // Panels other than the outputTextPane use outputTextArea.
                if ( outputTextPane.isShowing() ) {
                    outputScrollPane.remove(outputTextPane);                
                    outputScrollPane.repaint();
                    outputScrollPane.getViewport().add(outputTextArea);
                    outputScrollPane.validate();             
                }     
                
                // By clicking optionButtons[0] we should initialize the virtual breathalyzer.
                if ( clickedButton.equals(optionButtons[0]) ) {                    
                    
                    userTextArea.setText(""); 
                        
                    if ( optionButtons[1].isSelected() ) {                
                        
                        // Note that the userListPanel is used by both virtual breathalyzer and calorie calculator.
                        // Therefore we need to slightly modify it every single time we switch between the two options.
                        // That includes the need to display relevant outputButtons and to set the relevant items for the nameComboBox.
                        userListPanel.remove(outputButtons[1]);
                        userListPanel.repaint();
                        userListPanel.add(outputButtons[0]);
                        
                        frequencyComboBox.setEnabled(true);
                        durationComboBox.setEnabled(true);
                        activityComboBox.setEnabled(false);
                        
                        clickedButton.setSelected(true);
                        optionButtons[1].setSelected(false);
                        setNewComboBoxItems ();
                        
                    }
                        
                    if ( !userPanel.isShowing() ) {
                            mainPanel.removeAll();
                            mainPanel.repaint();
                            mainPanel.add(userPanel, BorderLayout.CENTER);
                            mainPanel.validate();                            
                    }                  
                
                // Similarly by clicking optionButtons[1] we should initialize the calorie calculator.
                } else if ( clickedButton.equals(optionButtons[1]) ) {
                        
                    userTextArea.setText(""); 
                        
                    if ( optionButtons[0].isSelected() ) {                
                        
                        userListPanel.remove(outputButtons[0]);
                        userListPanel.repaint();
                        userListPanel.add(outputButtons[1]);
                
                        frequencyComboBox.setEnabled(false);
                        durationComboBox.setEnabled(false);
                        activityComboBox.setEnabled(true);
                        
                        clickedButton.setSelected(true);
                        optionButtons[0].setSelected(false);
                        setNewComboBoxItems ();
                    }
                    
                    if ( !userPanel.isShowing() ) {
                            mainPanel.removeAll();
                            mainPanel.repaint();
                            mainPanel.add(userPanel, BorderLayout.CENTER);
                            mainPanel.validate();                            
                    }       
                
                // After clicking optionButtons[2] we should be able to see the manager's panel.
                } else if( clickedButton.equals(optionButtons[2]) && !managerPanel.isShowing() ) {
                   
                    mainPanel.removeAll();
                    mainPanel.repaint();
                    mainPanel.add(managerPanel, BorderLayout.CENTER);
                    mainPanel.validate();                    
                
                }                
                
            }             
            
        }
    } 
    
    
    // This class adds a product to the products list (it adds a product to userTextArea).
    class AddProductHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            try {
                String name = (String)nameComboBox.getSelectedItem();
                
                // If nameComboBox shows "Twój produkt" read ml and percentage text fields.
                if ( name.equals("Twój produkt") ) {
                    
                    // Verify that ml and percentage attributes are entered properly.
                    List<String> errors = new ArrayList<String>();
                
                    int ml = 1;
                    try {
                        ml = Integer.parseInt( mlTextField.getText().trim() );
                    } catch (NumberFormatException nfe) {
                        errors.add("Pole 'Pojemność' musi być liczbą całkowitą.");
                    }
                    if( ml != 1 && (ml<1 || ml>1000 ) ) {            
                        errors.add("'Pojemność' musi być liczbą całkowitą od 1 do 1000.");           
                    }
        
                    double percentage = 0.1;
                    try {
                        percentage = Double.parseDouble( percentageTextField.getText().trim() );
                    } catch (NumberFormatException nfe) {
                        errors.add("Pole 'Alkohol' musi być liczbą całkowitą.");
                    }
                        if( percentage != 0.1 && (percentage<0.1 || percentage>100.0 ) ) {            
                        errors.add("'Alkohol' musi być liczbą rzeczywistą od 0.1 do 100.");           
                    }
                
                    if( !errors.isEmpty() ) {                
                        throw new FormException(errors);
                    }
                   
                    name = name + " " + ml + "ml " + percentage + "%";    
                }
            
                int productQuantity = (Integer)quantityComboBox.getSelectedItem();                
                name = name + " x " + productQuantity + "\n";
            
                userTextArea.append(name);
                outputTextArea.setText("Dodano nowy produkt do listy.\n" + name);              
        
            } catch (FormException fe) {
                displayErrors(fe);
            }
            
        }        
    }
    
    
    // This class removes all products from the products list (it clears userTextArea).
    class RemoveAllProductsHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            if ( !userTextArea.getText().isEmpty() ) {
                outputTextArea.setText("Usunięto wszystkie produkty z listy.");
                userTextArea.setText("");                
            } else {
                outputTextArea.setText("Wystąpiły następujące błędy:\n");
                outputTextArea.append("Liata nie zawiera żadnych produktów.");
            }
            
        }           
    }
    
    
    // This class calculates user's Blood Alcohol Content.
    class GetAlcoholContentHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            List<String> errors = new ArrayList<String>();           
            
            try {
                
                String text = userTextArea.getText();                       
                
                if ( text.isEmpty() ) {
                    errors.add("Lista nie zawiera żadnych produktów.");
                    throw new FormException(errors);
                }             
                        
                String[] lines = text.split("\n"); 
                
                // Consider each line of the text separately.
                double gramsOfAlcohol = 0;
                for (String nextLine : lines) {
                
                    String[] elements = nextLine.split(" ");                   
                    
                    double productPercentage;
                    String ml;
                    if ( elements[0].equals("Twój") ) {
                        
                        ml = elements[elements.length-4].replace("ml", "");
                        String percentageStr = elements[elements.length-3].replace("%", "");
                        productPercentage = Double.parseDouble(percentageStr);
                        
                    } else {
                    
                        // Name of a product may consist of several separate words. 
                        String productName = elements[0];
                        for (int j=1; j<elements.length-3; j++) {
                            productName = productName + " " + elements[j];
                        }
                    
                        AlcoholicProduct product = (AlcoholicProduct)productModel.getProduct(productName);                 
                        String productMeasure = product.getMeasure();
                        ml = productMeasure.substring(0, productMeasure.length()-2);
                        productPercentage = product.getAlcohol();                        
                
                    }
                    
                    // Calculate how many grams of alcohol a product contains. 
                    double productAlcoholBeforeRound = Integer.parseInt(ml.trim()) * (productPercentage/100) * 0.79;
                    double productAlcohol = Math.round(productAlcoholBeforeRound*100.0)/100.0;
                    
                    String quantity = elements[elements.length-1];
                    gramsOfAlcohol = gramsOfAlcohol + productAlcohol * Integer.parseInt(quantity.trim());
                }
                
                //Read data entered by a user.
                int height = (Integer)heightComboBox.getSelectedItem();
                int weight = (Integer)weightComboBox.getSelectedItem();
                int age = (Integer)ageComboBox.getSelectedItem();
                String gender = (String)genderComboBox.getSelectedItem();
                String frequencyStr = (String)frequencyComboBox.getSelectedItem();
                double duration = (Double)durationComboBox.getSelectedItem();         
                
                // Use data entered to get required factors.
                double drinkingFrequency;
                if ( frequencyStr.equals("często") ) {
                    drinkingFrequency = 0.2;
                } else if ( frequencyStr.equals("zwyczajnie") ) {
                    drinkingFrequency = 0.15;
                } else {
                    drinkingFrequency = 0.1;
                }
                
                // Use Watson's formula to calculate Total Body Water.
                double totalBodyWater;
                if ( gender.equals("M") ) {
                    totalBodyWater = 2.447 - (0.09156*age) + (0.1074*height) + (0.3362*weight);
                } else {
                    totalBodyWater = -2.097 + (0.1069*height) + (0.2466*weight);
                }
                
                // Use Widmark's Formula to calculate Blood Alcohol Content.
                double bloodAlcoholContent = (gramsOfAlcohol * 0.806) / totalBodyWater - (drinkingFrequency * duration);
                if ( bloodAlcoholContent<0 ) {
                    bloodAlcoholContent = 0;
                }
                
                // Display results.
                outputTextArea.setText( "Twoja zawartość alkoholu we krwi to " + 
                        Math.round(bloodAlcoholContent*100.0)/100.0 + " promila.\n\n");    
                
                int hours = 1;
                while ( Math.round(bloodAlcoholContent*100.0)/100.0 > 0 ){
                    bloodAlcoholContent = bloodAlcoholContent - drinkingFrequency;
                    if ( Math.round(bloodAlcoholContent*100.0)/100.0 >= 0 ) {
                        outputTextArea.append( "Po " + hours + " godz. - " + 
                                Math.round(bloodAlcoholContent*100.0)/100.0 + " promila.\n\n");    
                    } else {
                        outputTextArea.append( "Po " + hours + " godz. nie będziesz już pod wpływem alkoholu.");
                    } 
                    hours++;
                }
                
                outputTextArea.setCaretPosition(0);
                
            } catch (FormException fe) {
                displayErrors(fe);
            }
            
        }
    }
    
    
    // This class calculates the caloric content of a meal.
    class GetCaloriesHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            List<String> errors = new ArrayList<String>();           
            
            try {
                
                String text = userTextArea.getText();                       
                
                if ( text.isEmpty() ) {
                    errors.add("Lista nie zawiera żadnych produktów.");
                    throw new FormException(errors);
                }    
                
                String[] lines = text.split("\n");
                
                // Consider each line of the text separately.
                int calories = 0;
                double protein = 0.0;
                double fat = 0.0;
                double carbohydrate = 0.0;
                for (String nextLine : lines) {
                
                    String[] elements = nextLine.split(" ");
                    
                    // Name of a product may consist of several separate words. 
                    String productName = elements[0];
                    for (int j=1; j<elements.length-3; j++) {
                        productName = productName + " " + elements[j];
                    }
                    
                    // Calculate the caloric content.
                    Product product = productModel.getProduct(productName);
                    String quantityStr = elements[elements.length-1]; 
                    int quantity = Integer.parseInt(quantityStr.trim());
                       
                    calories = calories + product.getCalories() * quantity;
                    protein = protein + product.getProtein() * quantity;
                    fat = fat + product.getFat() * quantity;
                    carbohydrate = carbohydrate + product.getCarbohydrate() * quantity;                   
                                    
                }              
                
                // Display results.
                outputTextArea.setText( "Twój posiłek zawiera:\n" + calories + " kcal\n" 
                        + Math.round( protein*10.0 )/10.0 + " g białka\n"
                        + Math.round( fat*10.0 )/10.0 + " g tłuszczu\n" 
                        + Math.round( carbohydrate*10.0 )/10.0 + " g węglowodanów");
                
                //Read data entered by a user.
                int height = (Integer)heightComboBox.getSelectedItem();
                int weight = (Integer)weightComboBox.getSelectedItem();
                int age = (Integer)ageComboBox.getSelectedItem();
                String gender = (String)genderComboBox.getSelectedItem();
                String activity = (String)activityComboBox.getSelectedItem();
                
                // Use data entered to get required factors.
                double activityFactor;
                if ( activity.equals("żadna") ) {
                    activityFactor = 1.2;
                } else if ( activity.equals("mała") ) {
                    activityFactor = 1.375;
                } else if ( activity.equals("umiarkowana") ) {
                    activityFactor = 1.55;
                } else if ( activity.equals("duża") ) {
                    activityFactor = 1.725;
                } else {
                    activityFactor = 1.9;
                }
                
                // Calculate Basal Metabolic Rate.
                double basalMetabolicRate;
                if ( gender.equals("M") ) {
                    basalMetabolicRate = 66.47 + (13.7 * weight) + (5 * height) - (6.76 * age);                    
                } else {
                    basalMetabolicRate = 655.1 + (9.56 * weight) + (1.85 * height) - (4.68 * age);                    
                }
                
                // Use Harris Benedict Formula to determine total daily calorie needs.
                int totalDailyCalorieNeeds = (int)(basalMetabolicRate * activityFactor);
                
                outputTextArea.append( "\n\nAby utrzymać obecną wagę potrzebujesz\n" 
                        + totalDailyCalorieNeeds + " kalorii dziennie " );
                
                // Calculate the required blend in terms of carbohydrates, fats and protein.
                double proteinNeeded = Math.round( ( (totalDailyCalorieNeeds*0.13)/4 )*10.0 )/10.0;
                double fatNeeded = Math.round( ( (totalDailyCalorieNeeds*0.3)/9 )*10.0 )/10.0;
                double carbohydrateNeeded = Math.round( ( ( totalDailyCalorieNeeds*0.57)/4 )*10.0 )/10.0;
                
                // Display results.
                outputTextArea.append( "dostarczanych przez:\n" + proteinNeeded + " g białka\n" 
                        + fatNeeded + " g tłuszczy\n" + carbohydrateNeeded + " g węglowodanów\n\n" );
                outputTextArea.append( "Twój posiłek zaspokaja:\n"
                        + Math.round( ((double)calories/totalDailyCalorieNeeds)*10000.0 )/100.0 + " % dziennego zapotrzebowania na kalorie\n"
                        + Math.round( (protein/proteinNeeded)*10000.0 )/100.0 + " % dziennego zapotrzebowania na białko\n" 
                        + Math.round( (fat/fatNeeded)*10000.0 )/100.0 + " % dziennego zapotrzebowania na tłuszcze\n" 
                        + Math.round( (carbohydrate/carbohydrateNeeded)*10000.0 )/100.0 + " % dziennego zapotrzebowania na węglowodany" );
            
            } catch (FormException fe) {
                displayErrors(fe);
            }                        
            
        }
    }
    
    
    // Adds a new product to existing data.
    class AddNewProductHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            // Read data entered by a manager.
            String productName = managerPanelTextFields.get(0).getText().trim();
            String productMeasure = managerPanelTextFields.get(1).getText().trim();
            String productCalories = managerPanelTextFields.get(2).getText().trim();
            String productProtein = managerPanelTextFields.get(3).getText().trim();
            String productFat = managerPanelTextFields.get(4).getText().trim();
            String productCarbohydrate = managerPanelTextFields.get(5).getText().trim();
            String productAlcohol = managerPanelTextFields.get(6).getText().trim();
            
            try {
                // Call a method that actually adds a product.
                Product product = productModel.addProduct( productName, productMeasure, productCalories, 
                        productProtein, productFat, productCarbohydrate, productAlcohol  );
                
                // Prepare a message to be displayed.
                outputTextArea.setText("Dodano produkt o nazwie '" + productName + "'\n\n");
                
                String name = product.getName() + " (" + product.getMeasure() + "):\n" 
                        + product.getCalories() + " kcal\n" + product.getProtein() + " g białka\n"
                        + product.getFat() + " g tłuszczu\n" + product.getCarbohydrate() + " g węglowodanów";
                
                if ( product instanceof AlcoholicProduct ) {
                    AlcoholicProduct alcoholicProduct = (AlcoholicProduct)product;
                    name = name + "\n" + alcoholicProduct.getAlcohol() + " % alkoholu";
                }
                
                outputTextArea.append(name);                
                
                // As the list of stored products has changed, set new items for the nameComboBox.
                setNewComboBoxItems ();
            
            } catch (FormException fe) {                
                displayErrors(fe);                
            }            
            
            
        }    
    }    
    
    
    // Modifies a product in existing data.
    class ModifyProductHandler implements ActionListener {
       
        public void actionPerformed(ActionEvent e) {
            
            // Read data entered by a manager.
            String productName = managerPanelTextFields.get(0).getText().trim();
            String productMeasure = managerPanelTextFields.get(1).getText().trim();
            String productCalories = managerPanelTextFields.get(2).getText().trim();
            String productProtein = managerPanelTextFields.get(3).getText().trim();
            String productFat = managerPanelTextFields.get(4).getText().trim();
            String productCarbohydrate = managerPanelTextFields.get(5).getText().trim();
            String productAlcohol = managerPanelTextFields.get(6).getText().trim();
            
            try {
                // Call a method that actually modifies a product.
                Product product = productModel.modifyProduct( productName, productMeasure, productCalories,
                        productProtein, productFat, productCarbohydrate, productAlcohol );
                
                // Prepare a message to be displayed.
                outputTextArea.setText("Zmodyfikowano produkt o nazwie '" + productName + "'\n\n");
                
                String name = product.getName() + " (" + product.getMeasure() + "):\n" 
                        + product.getCalories() + " kcal\n" + product.getProtein() + " g białka\n"
                        + product.getFat() + " g tłuszczu\n" + product.getCarbohydrate() + " g węglowodanów";
                
                if ( product instanceof AlcoholicProduct ) {
                    AlcoholicProduct alcoholicProduct = (AlcoholicProduct)product;
                    name = name + "\n" + alcoholicProduct.getAlcohol() + " % alkoholu";
                }
                
                outputTextArea.append(name); 
                
                // As the list of stored products has changed, set new items for the nameComboBox.
                setNewComboBoxItems ();
                
            } catch (FormException fe) {                
                displayErrors(fe);                
            }
            
        }        
    }
    
    
    // Removes a product from existing data.
    class DeleteExistingProductHandler implements ActionListener {
       
        public void actionPerformed(ActionEvent e) {            
            
            // Raad a product's name.
            String productName = managerPanelTextFields.get(0).getText().trim();
                
            try {
                // Call a method that actually removes a product.
                Product product = productModel.removeProduct(productName);                        
                
                // Prepare a message to be displayed.
                outputTextArea.setText("Usunięto produkt o nazwie '" + productName + "'\n\n");
                
                String name = product.getName() + " (" + product.getMeasure() + "):\n" 
                        + product.getCalories() + " kcal\n" + product.getProtein() + " g białka\n"
                        + product.getFat() + " g tłuszczu\n" + product.getCarbohydrate() + " g węglowodanów";
                
                if ( product instanceof AlcoholicProduct ) {
                    AlcoholicProduct alcoholicProduct = (AlcoholicProduct)product;
                    name = name + "\n" + alcoholicProduct.getAlcohol() + " % alkoholu";
                }
                
                outputTextArea.append(name);
                
                // As the list of stored products has changed, set new items for the nameComboBox.
                setNewComboBoxItems ();
                
            } catch (FormException fe) {
                displayErrors(fe);                
            }            
            
        }        
    }
    
    
    // Displays a chosen product.
    class ShowChosenProductHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            // Raad a product's name.
            String productName = managerPanelTextFields.get(0).getText().trim();
            
            try {
                // Call a method that actually gets a product.
                Product product = productModel.getProduct(productName);                        
                
                // Prepare a message to be displayed.
                outputTextArea.setText("Wyświetlasz wybrany produkt.\n\n");
                
                String name = product.getName() + " (" + product.getMeasure() + "):\n" 
                        + product.getCalories() + " kcal\n" + product.getProtein() + " g białka\n"
                        + product.getFat() + " g tłuszczu\n" + product.getCarbohydrate() + " g węglowodanów";
                
                if ( product instanceof AlcoholicProduct ) {
                    AlcoholicProduct alcoholicProduct = (AlcoholicProduct)product;
                    name = name + "\n" + alcoholicProduct.getAlcohol() + " % alkoholu";
                }
                
                outputTextArea.append(name);
                
            } catch (FormException fe) {
                displayErrors(fe);                
            }
            
        }       
    }
    
    
    // Displays a list of all stored products. 
    class ShowAllProductsHandler implements ActionListener {
        
        public void actionPerformed(ActionEvent e) {            
            
            outputTextArea.setText("Lista wszystkich produktów:\n\n");
            
            // Call a method that actually returns a list of all products.
            List<Product> products = productModel.getAllProducts();
            
            // Display all products.
            for (Product nextProduct : products) {
            
                String name = nextProduct.getName() + " (" + nextProduct.getMeasure() + "):\n" 
                        + nextProduct.getCalories() + " kcal\n" + nextProduct.getProtein() + " g białka\n"
                        + nextProduct.getFat() + " g tłuszczu\n" + nextProduct.getCarbohydrate() + " g węglowodanów";
                
                if ( nextProduct instanceof AlcoholicProduct ) {
                    AlcoholicProduct nextAlcoholicProduct = (AlcoholicProduct)nextProduct;
                    name = name + "\n" + nextAlcoholicProduct.getAlcohol() + " % alkoholu";
                }
                
                outputTextArea.append(name + "\n\n");    
            }
            
            outputTextArea.setCaretPosition(0);
            
        }    
    }
    
    
    // Displays a list of all alcoholic products. 
    class ShowAllAlcoholicProductsHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            outputTextArea.setText("Lista wszystkich produktów alkoholowych:\n\n");
            
            // Call a method that actually returns a list of all products.
            List<Product> products = productModel.getAllProducts();    
            
            // Display only alcoholic products.
            for (Product nextProduct : products) {
                
                if ( nextProduct instanceof AlcoholicProduct ) {
                    AlcoholicProduct nextAlcoholicProduct = (AlcoholicProduct)nextProduct;                    
                
                    String name = nextAlcoholicProduct.getName() + " (" + nextAlcoholicProduct.getMeasure() + "):\n" 
                        + nextAlcoholicProduct.getCalories() + " kcal,\n" + nextAlcoholicProduct.getProtein() + " g białka\n"
                        + nextAlcoholicProduct.getFat() + " g tłuszczu\n" + nextAlcoholicProduct.getCarbohydrate() + " g węglowodanów\n"
                        + nextAlcoholicProduct.getAlcohol() + " % alkoholu";
                    outputTextArea.append(name + "\n\n");
                }                
                    
            }
            
            outputTextArea.setCaretPosition(0);
            
        }            
    }
    
    
    // Displays a list of all non alcoholic products. 
    class ShowAllNonAlcoholicProductsHandler implements ActionListener {
         
        public void actionPerformed(ActionEvent e) {
            
            outputTextArea.setText("Lista wszystkich produktów niealkoholowych:\n\n");
            
            // Call a method that actually returns a list of all products.
            List<Product> products = productModel.getAllProducts();    
            
            // Display only non alcoholic products.
            for (Product nextProduct : products) {
            
                if ( !(nextProduct instanceof AlcoholicProduct) ) {
                    String name = nextProduct.getName() + " (" + nextProduct.getMeasure() + "):\n" 
                        + nextProduct.getCalories() + " kcal\n" + nextProduct.getProtein() + " g białka\n"
                        + nextProduct.getFat() + " g tłuszczu\n" + nextProduct.getCarbohydrate() + " g węglowodanów";
                    outputTextArea.append(name + "\n\n");
                }                
                    
            }
            
            outputTextArea.setCaretPosition(0);
            
        }     
    }
    
    
    // Displays a description of a chosen topic 
    // Such as informations about the application, informations about algorithms etc.
    class ShowDescriptionHandler implements ActionListener {
       
        public void actionPerformed(ActionEvent e) {            
            
            // Names of files, containing text to be displayed, are visible on buttons. 
            JButton button = (JButton)e.getSource();
            String buttonText = button.getText();
            String filePath = "resources/" + buttonText.trim() + ".rtf";
            
            InputStream in = null;
            try {
                File file = new File(filePath);
                in = new FileInputStream(file);
                dsd.remove(0, dsd.getLength());
                kit.read(in,dsd,0);                   
                outputTextPane.setDocument(dsd);
                outputTextPane.setCaretPosition(0);
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
            } finally {
                try {
                    in.close();
                } catch (IOException ioe) {
                    System.out.println(ioe.getMessage());
                }
            } 
            
        }        
    }
    
    
    // Displays a list of errors that have occurred during processing a form.
    private void displayErrors (FormException fe) {
        
        outputTextArea.setText(fe.getMessage() + "\n\n");
                
        List<String> errors = fe.getErrors();
        for (String nextError : errors) {
            outputTextArea.append(nextError + "\n");    
        }
                
    }
    
    
    // Sets a new list of items for a nameComboBox. It is used after each modification of existing data.
    private void setNewComboBoxItems () {
        
        nameComboBox.removeAllItems();
        if (optionButtons[0].isSelected()) {
           nameComboBox.addItem("Twój produkt"); 
        }
        
        List<Product> products = productModel.getAllProducts();
        
        for (Product nextProduct : products) {
            if (optionButtons[1].isSelected()) {
                String comboBoxItem = nextProduct.getName() + " (" + nextProduct.getMeasure() + ")";
                nameComboBox .addItem(comboBoxItem);                 
            } else {               
                if (nextProduct instanceof AlcoholicProduct) {
                    String comboBoxItem = nextProduct.getName() + " (" + nextProduct.getMeasure() + ")";
                    nameComboBox .addItem(comboBoxItem); 
                }
            }
        }
        
    }       
   

}


